package emulator.hardware.clock;

import java.util.Observer;


public class ClockImplementation implements Clock {

	private static int max_handles = 10;
	private static long sync_threshhold = 10;

	private TickScales tick_scales = new TickScales();
	private TickHelper helper;
	private long[] handle_ticks = null;
	private int act_handles = 0;
	
	public ClockImplementation() {
		tick_scales.init();
		handle_ticks = new long [max_handles];
		helper = new TickHelper(handle_ticks, act_handles);
	}

	@Override
	public synchronized ClockHandle acquireHandle() {
		ClockHandleImplementation clock_handle = new ClockHandleImplementation(this);
		int new_handle_id = act_handles++;
		handle_ticks[new_handle_id] = 0;
		clock_handle.setHandle(new_handle_id);
		helper.setHandleCount(act_handles);
		return clock_handle;
	}

	@Override
	public synchronized void releaseHandle(ClockHandle handle) {
		handle_ticks[((ClockHandleImplementation)handle).getHandle()] = -1;
	}

	public long getFrequency() {
		return tick_scales.getClockSpeed();
	}

	public long getTicks() {
		return handle_ticks[0];
	}

	public synchronized void countTicks(ClockHandleImplementation clock_handle, int ticks_elapsed)
	{
		// Count ticks
		handle_ticks[clock_handle.getHandle()] += ticks_elapsed;
		helper.calcMinMax();
		if (helper.getMax() > Long.MAX_VALUE/2)
			helper.rebaseTicks();
		// Sync clients
//		if (helper.getMax() - helper.getMin() > sync_threshhold)
		if (helper.getMin() + sync_threshhold < handle_ticks[clock_handle.getHandle()])
		{
			notifyAll();
			try {
				while (helper.getMin() + sync_threshhold < handle_ticks[clock_handle.getHandle()])
					wait();
			} catch (InterruptedException e) {
			}
		}
/*		if (helper.getMax() - handle_ticks[clock_handle.getHandle()] <= sync_threshhold)
			notifyAll(); */
		// Calc actual frequency
		if (clock_handle.getHandle() == 0)
			tick_scales.measureTicks(getTicks());
	}

	public void addClockSpeedObserver(Observer observer) {
		tick_scales.addObserver(observer);
	}
	
}
